package utils

import (
	"context"
	"fmt"
	"time"
)

type Queue struct {
	data   chan interface{}
	closed bool
}

// NewQueue 新建一个定长队列
func NewQueue(size uint64) (*Queue, error) {
	if size <= 0 {
		return nil, fmt.Errorf("size can not <= 0")
	}
	return &Queue{
		data:   make(chan interface{}, size),
		closed: false,
	}, nil
}

// Push 向队列中插入一条或多条数据，旧的数据单元有可能丢失！！！
func (q *Queue) Push(items ...interface{}) {
	l := len(items)
	for i := 0; i < l && !q.closed; {
		select {
		case q.data <- items[i]:
			i++
		default:
			select {
			case <-q.data:
			default:
			}
		}
	}
}

// TryPushOne 尝试插入一条数据，立即返回成功或失败
func (q *Queue) TryPushOne(item interface{}) bool {
	if q.closed {
		return false
	}
	select {
	case q.data <- item:
		return true
	default:
		return false
	}
}

// PushWait 向队列中插入一条或多条数据，空间充满时，将会阻塞等待
func (q *Queue) PushWait(items ...interface{}) {
	l := len(items)
	for i := 0; i < l && !q.closed; {
		select {
		case q.data <- items[i]:
			i++
		}
	}
}

// Pop 从队列中拿出一条数据，FIFO原则
func (q *Queue) Pop() (interface{}, bool) {
	select {
	case item := <-q.data:
		return item, true
	default:
		return nil, false
	}
}

// PopWait 从队列中拿出一条数据，阻塞等待
func (q *Queue) PopWait(td time.Duration) (interface{}, error) {
	if td <= 0 {
		select {
		case item := <-q.data:
			return item, nil
		}
	} else {
		ctx, cancel := context.WithTimeout(context.Background(), td)
		defer cancel()
		select {
		case item := <-q.data:
			return item, nil
		case <-ctx.Done():
			return nil, ctx.Err()
		}
	}
}

// PopAllWalk 逐个处理
func (q *Queue) PopAllWalk(fn func(item interface{})) {
	for doPop := true; doPop; {
		select {
		case item := <-q.data:
			fn(item)
		default:
			doPop = false
		}
	}
}

// PopAll 从队列中拿出所有的数据
func (q *Queue) PopAll() (ret []interface{}) {
	q.PopAllWalk(func(item interface{}) {
		ret = append(ret, item)
	})
	return
}

// Pause 暂停队列，忽略写入，能读出
func (q *Queue) Pause(b bool) {
	q.closed = b
}
